﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MediaPortal.GUI.Library;
using SevenZip;
using Cornerstone.MP;

namespace MyEmulators2
{
    class updatedPresenter
    {
        ImageSwapper backdrop = null;
        AsyncImageResource cover = null;
        AsyncImageResource backCover = null;
        AsyncImageResource titleScreen = null;
        AsyncImageResource inGameScreen = null;

        GUIFacadeControl facade = null;
        int facadeIndex = 0;
        GUIButtonControl buttonView = null;

        int lastitemIndex = 0;
        ExtendedGUIListItem lastItem = null;

        ViewState currentView = ViewState.Emulators;
        string currentSQLTail = "";
        GameProperty currentGameProperty = GameProperty.none;
        bool isFavourite = false;

        bool startWithFavourites = false;
        bool startWithPCGames = false;

        DBItem startupItem = null;
        bool launchStartupItem = false;

        public updatedPresenter(AsyncImageResource cover, AsyncImageResource backCover, AsyncImageResource titleScreen, AsyncImageResource inGameScreen)
        {
            this.cover = cover;
            this.backCover = backCover;
            this.titleScreen = titleScreen;
            this.inGameScreen = inGameScreen;

            //check startup options
            startWithFavourites = Options.Instance.GetBoolOption("startwithfavourites");
            startWithPCGames = Options.Instance.GetBoolOption("onlyshowpcgames");
            if (startWithFavourites)
                Logger.LogDebug("Starting with favourites");
            else if (startWithPCGames)
                Logger.LogDebug("Starting with PC Games");

        }

        public void Load(GUIFacadeControl facade, GUIButtonControl buttonView, ImageSwapper backdrop)
        {
            Load(facade, buttonView, backdrop, null, false);
        }

        public void Load(GUIFacadeControl facade, GUIButtonControl buttonView, ImageSwapper backdrop, DBItem startupItem, bool launch)
        {
            this.facade = facade;
            this.buttonView = buttonView;
            this.backdrop = backdrop;


            clearGUIProperties();

            if (GUIWindowManager.ActiveWindow == GUIWindowManager.GetPreviousActiveWindow())
            {
                launchStartupItem = false;
                Refresh(); //Catch when MP is refreshing plugin after resizing/restoring and maintain current view
            }
            else
            {
                this.startupItem = startupItem;
                this.launchStartupItem = launch;
                fillEmulators(0);
            }
        }

        //Have we selected an emuator to filter DB views against
        public bool EmulatorSelected()
        {
            return (lastItem != null && currentView != ViewState.Emulators);
        }

        public void ItemSelected()
        {

            ExtendedGUIListItem selectedListItem = facade.SelectedListItem as ExtendedGUIListItem;
            int index = facade.SelectedListItemIndex;
            if (selectedListItem.AssociatedEmulator != null)
            {
                emulatorSelected(selectedListItem, 0, index);
            }
            else if (selectedListItem.AssociatedGame != null)
            {
                gameSelected(selectedListItem, 0, index);
            }
            else if (selectedListItem.IsBackDots)
                GoBack();
                
        }

        void fillEmulators(int index)
        {
            lastItem = null;
            lastitemIndex = 0;

            if (startupItem != null)
            {
                if (startupItem is Emulator)
                {
                    Emulator emu = startupItem as Emulator;
                    FillSomeGames(string.Format("WHERE parentemu='{0}' ORDER BY title ASC", emu.UID), GameProperty.none, index, ViewState.Games);
                    setLayout(emu.View);
                    return;
                }

                if (startupItem is Game)
                {
                    Game game = startupItem as Game;
                    FillSomeGames(string.Format("WHERE gameid='{0}' ORDER BY title ASC", game.GameID), GameProperty.none, index, ViewState.Games);
                    setLayout(game.ParentEmulator.View);
                    if (launchStartupItem)
                    {
                        launchStartupItem = false; //otherwise continuous loop of rom launch
                        Executor.Instance.launchGame(game);
                    }
                    return;
                }
            }

            if (startWithFavourites)
            {
                FillSomeGames("WHERE favourite='True' ORDER BY title ASC", GameProperty.none, index, ViewState.Games);
                setLayout(Options.Instance.GetIntOption("viewfavourites"));
            }
            else if (startWithPCGames)
            {
                FillSomeGames("WHERE parentemu='-1' ORDER BY title ASC", GameProperty.none, index, ViewState.Games);
                setLayout(Options.Instance.GetIntOption("viewpcgames"));
            }
            else
            {
                setItemsToFacade(DB.Instance.GetEmulatorsAndPC(), ViewState.Emulators, null, 0, index);
                //setLayout must be called after setting facade items and lastItem info
                setLayout(Options.Instance.GetIntOption("viewemus"));
            }
        }

        void emulatorSelected(ExtendedGUIListItem selectedListItem, int index, int parentIndex)
        {
            if (selectedListItem.AssociatedEmulator == null)
                return;

            if (!setItemsToFacade(DB.Instance.GetGames(selectedListItem.AssociatedEmulator), ViewState.Games, selectedListItem, parentIndex, index))
                return;
            lastItem = selectedListItem;
            lastitemIndex = parentIndex;

            //setLayout must be called after setting facade items and lastItem info
            if (selectedListItem.AssociatedEmulator.UID > -1)
                setLayout(selectedListItem.AssociatedEmulator.View);
            else if (selectedListItem.AssociatedEmulator.isPc())
                setLayout(Options.Instance.GetIntOption("viewpcgames")); //PC layout is stored in xml
        }

        void gameSelected(ExtendedGUIListItem selectedListItem, int index, int parentIndex)
        {
            Game selectedGame = selectedListItem.AssociatedGame;
            if (selectedGame == null)
                return;
            if (!selectedGame.IsGoodmerge || !selectedGame.ParentEmulator.EnableGoodmerge || selectedGame.LaunchFile.Trim() != "")
            {
                Executor.Instance.launchGame(selectedGame);
                return;
            }

            Game[] goodMergeGames = Extractor.Instance.ViewGames(selectedGame);
            if (goodMergeGames == null) //Invalid archive
                return;
            else if (goodMergeGames.Length == 1)
                Executor.Instance.launchGame(selectedGame);
            else if (goodMergeGames.Length > 0)
            {
                lastitemIndex = parentIndex;
                lastItem = selectedListItem;
                setItemsToFacade(goodMergeGames, ViewState.GoodMerge, lastItem, lastitemIndex, index);
            }
            else
                Conf_GlobalSettings.Instance.ShowMPDialog("Goodmerge error:\r\nThe selected archive is empty");
        }

        public void FillSomeGames(string sqlTail, GameProperty secondLabel, int index)
        {
            FillSomeGames(sqlTail, secondLabel, index, ViewState.Views, false);
        }

        public void FillSomeGames(string sqlTail, GameProperty secondLabel, int index, ViewState view)
        {
            FillSomeGames(sqlTail, secondLabel, index, view, false);
        }
        
        public void FillSomeGames(string sqlTail, GameProperty secondLabel, int index, ViewState view, bool isFavourite)
        {
            Game[] games = DB.Instance.GetGames(sqlTail, false);
            if (games.Length == 0)
                return; //show message

            Emulator currentEmulator = new Emulator(EmulatorType.manyemulators);
            if (currentView != ViewState.Emulators && lastItem != null)
            {
                if (lastItem.ViewState == ViewState.Views) //if entered goodmerge from views go back 1 level
                {
                    lastitemIndex = lastItem.ParentIndex;
                    lastItem = lastItem.Parent;
                }
                if (lastItem != null)
                {
                    if (lastItem.AssociatedEmulator != null)
                        currentEmulator = lastItem.AssociatedEmulator;
                    else if (lastItem.AssociatedGame != null)
                        currentEmulator = lastItem.AssociatedGame.ParentEmulator;
                }
            }

            //save current filter to enable navigation back
            currentSQLTail = sqlTail;
            currentGameProperty = secondLabel;

            List<GUIListItem> firstPart = new List<GUIListItem>();
            List<GUIListItem> secondPart = new List<GUIListItem>();

            clearGUIProperties();
            GUIControl.ClearControl(Plugin.WINDOW_ID, facade.GetID);

            for (int i = 0; i < games.Length; i++)
            {
                if (!currentEmulator.isManyEmulators() && games[i].ParentEmulator.UID != currentEmulator.UID)
                    continue; //game doesn't match current emulator

                ExtendedGUIListItem item = games[i].CreateGUIListItem();
                item.ViewState = view;
                item.Parent = lastItem;
                item.ParentIndex = lastitemIndex;
                item.OnItemSelected += new GUIListItem.ItemSelectedHandler(onFacadeItemSelected);
                switch (secondLabel)
                {
                    case GameProperty.grade: { item.Label2 = "(" + games[i].Grade + "/10)"; break; }
                    case GameProperty.latestplay: { item.Label2 = "(" + games[i].Latestplay.ToShortDateString() + ")"; break; }
                    case GameProperty.playcount: { item.Label2 = "(" + games[i].Playcount + ")"; break; }
                    case GameProperty.year: { item.Label2 = "(" + games[i].Yearmade + ")"; break; }
                    case GameProperty.genre: { item.Label2 = "(" + games[i].Genre + ")"; break; }
                    case GameProperty.company: { item.Label2 = "(" + games[i].Company + ")"; break; }
                }
                if (item.Label2.Equals("(0)") || item.Label2.Equals("(0/10)") || item.Label2.Equals("(" + DateTime.MinValue.ToShortDateString() + ")") || item.Label2.Equals("()"))
                {
                    item.Label2 = "n/a";
                }
                if (Options.Instance.GetBoolOption("hidelabeldecorations"))
                {
                    item.Label2 = item.Label2.Replace("(", "").Replace(")", "");
                }

                if (!item.Label2.Equals("n/a"))
                {
                    firstPart.Add(item);
                }
                else
                {
                    secondPart.Add(item);
                }

            }

            //Put all the ones with empty Label2 in the end
            for (int i = 0; i < firstPart.Count; i++)
            {
                //facade.Add(item);
                GUIControl.AddListItemControl(Plugin.WINDOW_ID, facade.GetID, firstPart[i]);
            }
            for (int i = 0; i < secondPart.Count;i++ )
            {
                //facade.Add(item);
                GUIControl.AddListItemControl(Plugin.WINDOW_ID, facade.GetID, secondPart[i]);
            }
            setFacadeIndex(index);

            GUIControl.FocusControl(Plugin.WINDOW_ID, facade.GetID);
            currentView = view;
            if (isFavourite)
            {
                this.isFavourite = isFavourite;
                setLayout(Options.Instance.GetIntOption("viewfavourites"));
            }
            onFacadeItemSelected(facade.SelectedListItem, facade);
        }

        public bool GoBack()
        {
            if (!facade.Focus) //if viewing menu return focus to facade
            {
                GUIControl.FocusControl(Plugin.WINDOW_ID, facade.GetID);
                return true;
            }

            if (currentView == ViewState.Views)
            { 
                if (lastItem == null)
                {
                    fillEmulators(0); //view emulators
                    return true;
                }
                switch (lastItem.ViewState)
                {
                    case ViewState.Emulators:
                        emulatorSelected(lastItem, 0, lastitemIndex); //view games
                        break;
                    case ViewState.Games:
                        gameSelected(lastItem, 0, lastitemIndex); //view goodmerge
                        break;
                }
                return true;
            }

            if (lastItem == null) //exit plugin
                return false;
            if (lastItem.ViewState == ViewState.Emulators)
            {
                fillEmulators(lastitemIndex);
                return true;
            }
            else if (lastItem.ViewState == ViewState.Games)
            {
                if (lastItem.Parent == null) //started with favourites or pc games
                    fillEmulators(lastitemIndex);
                else
                    emulatorSelected(lastItem.Parent, lastitemIndex, lastItem.ParentIndex);
                return true;
            }
            else if (lastItem.ViewState == ViewState.Views)
            {
                FillSomeGames(currentSQLTail, currentGameProperty, lastitemIndex);
                return true;
            }
            return false;
        }

        public void LaunchDocument() 
        {
            if (facade == null)
                return;
            if (facade.SelectedListItem == null)
                return;

            ExtendedGUIListItem item = (ExtendedGUIListItem)facade.SelectedListItem;
            Logger.LogDebug("Opening {0} manual", item.Label);
            if (item.AssociatedEmulator != null)
                Executor.launchDocument(item.AssociatedEmulator);
            else if (item.AssociatedGame != null)
                Executor.launchDocument(item.AssociatedGame);
        }

        public void LaunchArchive()
        {
            if (facade == null)
                return;
            if (facade.SelectedListItem == null)
                return;

            ExtendedGUIListItem item = (ExtendedGUIListItem)facade.SelectedListItem;
            if (item.AssociatedGame != null && currentView != ViewState.GoodMerge)
            {
                if (item.AssociatedGame.ParentEmulator.EnableGoodmerge)
                {
                    Game clone = item.AssociatedGame.Clone();
                    clone.LaunchFile = "";
                    Executor.Instance.launchGame(clone);
                }
            }
        }

        public void ExtractArchive()
        {
            if (facade == null)
                return;
            if (facade.SelectedListItem == null)
                return;

            ExtendedGUIListItem item = (ExtendedGUIListItem)facade.SelectedListItem;
            if (item.AssociatedGame != null && currentView != ViewState.GoodMerge)
            {
                if (item.AssociatedGame.ParentEmulator.EnableGoodmerge)
                {
                    Game[] games = Extractor.Instance.ViewGames(item.AssociatedGame);
                    if (games == null) //invalid archive
                        return;
                    else if (games.Length == 1)
                        Executor.Instance.launchGame(games[0]);
                    else if (games.Length > 0)
                    {
                        lastItem = item;
                        lastitemIndex = facade.SelectedListItemIndex;
                        setItemsToFacade(games, ViewState.GoodMerge, lastItem, lastitemIndex, 0);
                    }
                    else
                        Conf_GlobalSettings.Instance.ShowMPDialog("Goodmerge error:\r\nThe selected archive is empty");
                }
            }
        }

        bool setItemsToFacade(DBItem[] items, ViewState viewState, ExtendedGUIListItem parent, int parentIndex, int selectedIndex)
        {
            if (items.Length == 0)
            {
                string type = items.GetType() == typeof(Emulator[]) ? "Emulators" : "Games";
                Conf_GlobalSettings.Instance.ShowMPDialog("No {0} to display", type);
                return false;
            }
            List<ExtendedGUIListItem> facadeItems = new List<ExtendedGUIListItem>();
            for (int i = 0; i < items.Length; i++)
            {
                ExtendedGUIListItem facadeItem = items[i].CreateGUIListItem();
                facadeItem.OnItemSelected += new GUIListItem.ItemSelectedHandler(onFacadeItemSelected);
                facadeItem.ViewState = viewState;
                facadeItem.Parent = parent;
                facadeItem.ParentIndex = parentIndex;
                facadeItems.Add(facadeItem);
            }

            clearGUIProperties();
            GUIControl.ClearControl(Plugin.WINDOW_ID, facade.GetID);
            for (int i = 0; i < facadeItems.Count; i++)
            {
                GUIControl.AddListItemControl(Plugin.WINDOW_ID, facade.GetID, facadeItems[i]);
            }
            currentView = viewState;
            setFacadeIndex(selectedIndex);

            GUIControl.FocusControl(Plugin.WINDOW_ID, facade.GetID);
            onFacadeItemSelected(facade.SelectedListItem, facade);
            return true;
        }

        void onFacadeItemSelected(GUIListItem item, GUIControl parent)
        {
            facadeIndex = facade.SelectedListItemIndex;
            ExtendedGUIListItem selectedItem = item as ExtendedGUIListItem;
            
            if (selectedItem.AssociatedEmulator != null)
            {
                selectedItem.AssociatedEmulator.SetGUIProperties();
                using (ThumbGroup thumbs = new ThumbGroup(selectedItem.AssociatedEmulator))
                    backdrop.Filename = thumbs.FanartDefaultPath;
            }
            else if (selectedItem.AssociatedGame != null)
            {
                //when in views set #coverflow_label to second label to show filter
                if (currentView == ViewState.Views && currentGameProperty != GameProperty.none)
                {
                    GUIPropertyManager.SetProperty("#coverflow_label", selectedItem.Label2);
                    selectedItem.AssociatedGame.SetGUIProperties(true);
                }
                else
                    selectedItem.AssociatedGame.SetGUIProperties();

                using (ThumbGroup thumbs = new ThumbGroup(selectedItem.AssociatedGame))
                {
                    backdrop.Filename = thumbs.FanartDefaultPath;
                    backCover.Filename = thumbs.BackCover.Path;
                    titleScreen.Filename = thumbs.TitleScreen.Path;
                    inGameScreen.Filename = thumbs.InGame.Path;
                }
            }
            else
            {
                clearGUIProperties();
                return;
            }
            cover.Filename = selectedItem.ThumbnailImage;
        }

        void clearGUIProperties()
        {
            //Image handlers
            backdrop.Filename = string.Empty;
            cover.Filename = string.Empty;
            backCover.Filename = string.Empty;
            titleScreen.Filename = string.Empty;
            inGameScreen.Filename = string.Empty;

            GUIPropertyManager.SetProperty("#item_title", "");
            GUIPropertyManager.SetProperty("#emulator_title", "");
            GUIPropertyManager.SetProperty("#coverflow_label", "");
            GUIPropertyManager.SetProperty("#game_description", "");
            GUIPropertyManager.SetProperty("#game_yearmade", "");
            GUIPropertyManager.SetProperty("#game_genre", "");
            GUIPropertyManager.SetProperty("#game_company", "");
            GUIPropertyManager.SetProperty("#game_latestplay", "");
            GUIPropertyManager.SetProperty("#game_playcount", "");
            GUIPropertyManager.SetProperty("#game_grade", "0");
        }

        public void setLayout(int view)
        {
            setLayout(view, false);
        }

        public void setLayout(int view, bool update)
        {
            //Don't attempt to switch to Filmstrip if already in that layout,
            //No point and prevents issues with Filmstrip selectedIndex not updating
            //straight away when using GUIMessage hack 

            if (facade.CurrentLayout == GUIFacadeControl.Layout.Filmstrip && view == 3)
                return;

            switch (view)
            {
                case 0:
                    {
                        facade.CurrentLayout = GUIFacadeControl.Layout.List;
                        buttonView.Label = Translator.getString(TranslatorString.viewlist);
                        break;
                    }
                case 1:
                    {
                        facade.CurrentLayout = GUIFacadeControl.Layout.SmallIcons;
                        buttonView.Label = Translator.getString(TranslatorString.viewicons);
                        break;
                    }
                case 2:
                    {
                        facade.CurrentLayout = GUIFacadeControl.Layout.LargeIcons;
                        buttonView.Label = Translator.getString(TranslatorString.viewlargeicons);
                        break;
                    }
                case 3:
                    {
                        facade.CurrentLayout = GUIFacadeControl.Layout.Filmstrip;
                        buttonView.Label = Translator.getString(TranslatorString.viewfilmstrip);
                        break;
                    }
                case 4:
                    {
                        facade.CurrentLayout = GUIFacadeControl.Layout.CoverFlow;
                        buttonView.Label = Translator.getString(TranslatorString.viewcoverflow);
                        break;
                    }
            }

            setFacadeIndex(facadeIndex);

            //only save layout state when user has changed layout
            if (!update)
                return;

            if (currentView == ViewState.Emulators)
            {
                Options.Instance.UpdateOption("viewemus", view);
            }
            else if (currentView == ViewState.Games)
            {
                if (lastItem == null) //we are on the top level and must have started with favourites/pc games
                {
                    if (startWithFavourites)
                        Options.Instance.UpdateOption("viewfavourites", view);
                    else if (startWithPCGames)
                        Options.Instance.UpdateOption("viewpcgames", view);
                    return;
                }
                Emulator emu = lastItem.AssociatedEmulator;
                if (emu != null)
                {
                    if (emu.UID > -1)
                    {
                        emu.View = view;
                        emu.Save();
                    }
                    else if (emu.isPc())
                        Options.Instance.UpdateOption("viewpcgames", view);
                }
            }
            else if (currentView == ViewState.Views && isFavourite)
            {
                Options.Instance.UpdateOption("viewfavourites", view);
            }
        }


        void setFacadeIndex(int index)
        {
            facade.SelectedListItemIndex = index;
            if (facade.CurrentLayout == GUIFacadeControl.Layout.Filmstrip)
            {
                //Workaround for filmstrip selected index
                GUIMessage msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_ITEM_SELECT, facade.WindowId, 0, facade.FilmstripLayout.GetID, index, 0, null);
                GUIGraphicsContext.SendMessage(msg);
            }
            else if (facade.CurrentLayout == GUIFacadeControl.Layout.CoverFlow)
            {
                facade.CoverFlowLayout.SelectCard(index); //this is dizzying on large lists, is there a better way??
            }
        }

        //Updates the GUI with latest info whilst maintaining current state
        public void Refresh()
        {
            if (lastItem == null)
                fillEmulators(facadeIndex);
            else if (currentView == ViewState.Views)
                FillSomeGames(currentSQLTail, currentGameProperty, facadeIndex);
            else if (lastItem.ViewState == ViewState.Emulators)
                emulatorSelected(lastItem, facadeIndex, lastitemIndex);
            else if (lastItem.ViewState == ViewState.Games || lastItem.ViewState == ViewState.Views)
                gameSelected(lastItem, facadeIndex, lastitemIndex);
        }

        public void ShowContextMenu(MenuPresenter menuHandler)
        {
            if (facade.Focus)
            {
                Game item = ((ExtendedGUIListItem)facade.SelectedListItem).AssociatedGame;
                if (item != null)
                    menuHandler.showGameDialog(Plugin.WINDOW_ID, item);
            }
        }

        public static void showNotifyDialog(string message)
        {
            MediaPortal.Dialogs.GUIDialogNotify dlg_error = (MediaPortal.Dialogs.GUIDialogNotify)GUIWindowManager.GetWindow((int)GUIWindow.Window.WINDOW_DIALOG_NOTIFY);
            if (dlg_error != null)
            {
                dlg_error.Reset();
                dlg_error.SetHeading("My Emulators");
                dlg_error.SetText(message);
                dlg_error.DoModal(GUIWindowManager.ActiveWindow);
            }
        }
    }
}
